﻿using Core;
using Microsoft.Xna.Framework.Graphics;
using Slock.GameObjects;
using Slock.Generation;
using Slock.Utility;
using Microsoft.Xna.Framework;
using Core.Input;
using Microsoft.Xna.Framework.GamerServices;
using System;

namespace Slock.Components
{
    /// <summary>
    /// Defines a block world, manages actors and does various other worldly tasks
    /// <remarks>
    /// Basically, the world will be composed of 16x16x128 size chunks called "patches".
    /// These patches will be generated at runtime before the game starts. Each patch will
    /// be placed in an octal tree scene graph and view culled against the camera frustum
    /// in real time for performance. Also upon level generation, each patch will be
    /// stepped through after being populated and we will check each block face for visibility
    /// afterwards, the only time we will need to run visibility maintenance is if we add or remove blocks
    /// </remarks>
    /// </summary>
    public class World
    {
        private float worldSize;
        // Y up
        private Vector3 sunLoc = new Vector3(-512, 768, -384);
        private GraphicsDevice device;
        private EngineContext engine;

        private TerrainPatch[, ,] terrainPatches;
        private Viewport viewport;
        private OctTreeNode root;


        private Effect debugShader;
        private Effect solidBlockShader;
        /// <summary>
        /// Initializes a new instance of World
        /// </summary>
        /// <param name="engine"></param>
        public World(EngineContext engine) : this(engine, WorldInfo.Initial_World_Dimensions)
        { }

        /// <summary>
        /// Initializes a new instance of World using a dimension 
        /// </summary>
        /// <param name="engine"></param>
        /// <param name="dimensions">The world size (in terrain patches)</param>
        public World(EngineContext engine, Point3 dimensions)
        {
            this.engine = engine;
            this.device = engine.GraphicsDevice;
            this.dimensions = dimensions;
            this.worldSize = dimensions.X * WorldInfo.Terrain_Patch_Dimension.X * WorldInfo.BLOCK_SIZE;
            this.root = new OctTreeNode(null, WorldInfo.Origin, worldSize);
            this.viewport = device.Viewport;
        }

        #region Methods

        /// <summary>
        /// Generates the initial block patches for the world
        /// </summary>
        /// <param name="factory"></param>
        public void GenerateWorld(IBlockFactory factory)
        {
            factory.GenerateWorld(this, dimensions, out terrainPatches);

            foreach (var patch in terrainPatches)
            {
                root.Add(patch);
            }

            this.debugShader = this.engine.Content.Load<Effect>(@"Shaders\Debug");

            this.solidBlockShader = this.engine.Content.Load<Effect>(@"Shaders\StaticVertexBuffer");
            this.solidBlockShader.Parameters["Texture"].SetValue(this.engine.Content.Load<Texture2D>(@"Textures\Block_Atlas"));

            this.solidBlockShader.Parameters["FogNear"].SetValue(WorldInfo.FAR_CLIP - 196);
            this.solidBlockShader.Parameters["FogFar"].SetValue(WorldInfo.FAR_CLIP - 96);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="gameTime"></param>
        public void Update(GameTime gameTime)
        {
            if (engine.IsActive || !Guide.IsVisible)
            {
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="gameTime"></param>
        public void Draw(GameTime gameTime)
        {

        }

        #endregion

        #region Properties

        /// <summary>
        /// Get the base engine context
        /// </summary>
        public EngineContext Engine
        {
            get { return engine; }
            set { engine = value; }
        }

        /// <summary>
        /// Get the graphcis device for rendering
        /// </summary>
        public GraphicsDevice GraphicsDevice
        {
            get { return device; }
            set { device = value; }
        }

        /// <summary>
        /// Get the size of the world
        /// </summary>
        public float WorldSize
        { get { return worldSize; } }

        /// <summary>
        /// Get the viewport
        /// </summary>
        public Viewport Viewport
        { get { return this.viewport; } }

        /// <summary>
        /// Get the player engine
        /// </summary>
        public PlayerEngine PlayerEngine
        { get { return this.playerEng; } }

        /// <summary>
        /// Get the scene manager
        /// </summary>
        public SceneManager SceneManager
        { get { return this.sceneManager; } }

        /// <summary>
        /// Get a terrain patch from the world
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="z"></param>
        /// <returns></returns>
        public TerrainPatch this[int x, int y, int z]
        { 
            get 
            { 
                try { return this.terrainPatches[x, y, z]; }
                catch (Exception) { return null; }
            } 
        }

        #endregion
    }
}
